package com.lry.basic.spring.ioc;

import com.lry.basic.spring.ioc.anno.*;
import com.lry.basic.spring.ioc.util.Common;
import org.junit.platform.commons.util.StringUtils;

import java.io.File;
import java.lang.reflect.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author:刘仁有
 * @desc: 解析配置类appConfig
 * @email:953506233@qq.com
 * @data:2019/11/18
 */
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor{
    @Override
    public void postProcessorBeanFactory(PreBuildBeanFactory factory) {
        Map<String, BeanDefinition> bdMap = factory.getBeanDefinitions();
        for (Map.Entry<String, BeanDefinition> entry:bdMap.entrySet()) {
            if(entry.getValue() instanceof AnnotationBeanDefinition){
                AnnotationBeanDefinition abd = (AnnotationBeanDefinition)entry.getValue();
                if(abd.getBeanClass().isAnnotationPresent(Configuration.class)&&
                abd.getBeanClass().isAnnotationPresent(ComponentScan.class)){
                    String pack = abd.getBeanClass().getAnnotation(ComponentScan.class).value();
                    parse(factory,pack);
                }
            }
        }
    }

    private void parse(PreBuildBeanFactory factory, String pack) {
        List<String>clazzStrs =  basePackageScan(pack);
        for (String clazzStr:clazzStrs) {
            try {
                Class<?> clazz = Class.forName(clazzStr);
                if (clazz.isAnnotationPresent(Component.class)) {
                    parseComponent(factory,clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public void parseComponent(PreBuildBeanFactory factory, Class<?> clazz) {
            GeneralBeanDefinition bd = new GeneralBeanDefinition();
            //处理Component的四个属性
            String bdName = handlerComponentValue(clazz, bd);
            //处理Component的method
            handlerComponentMethod(factory, clazz, bd);
            //处理Component的field
            handlerComponentField(factory, clazz, bd);
            //处理Component的Constructor
            handlerComponentConstructor(clazz, bd);
            bd.setBeanClass(clazz);
            factory.registerBeanDefinition(bdName, bd);
    }

    private void handlerComponentMethod(PreBuildBeanFactory factory,Class<?> clazz, GeneralBeanDefinition bd) {
        Method[]methods = clazz.getDeclaredMethods();
        for (Method method:methods) {
            if(method.isAnnotationPresent(Bean.class)){
                method.setAccessible(true);
                try {
                    parseComponent(factory,method.getReturnType());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void handlerComponentConstructor(Class<?> clazz, GeneralBeanDefinition bd) {
        Constructor<?>[] constructors = clazz.getConstructors();
        List<Object> cons = new ArrayList<>();
        for (Constructor con:constructors){
            if(con.isAnnotationPresent(Autowired.class)){
                Parameter[] parameters = con.getParameters();
                for (Parameter param:parameters){
                   if(param.isAnnotationPresent(Value.class)){
                       String value = param.getAnnotation(Value.class).value();
                       cons.add(Common.getBaseValue(param.getType(),value));
                   }else if(param.isAnnotationPresent(Autowired.class)){
                       cons.add(new BeanReference(param.getName()));
                   }
                }
                break;
            }
        }
        bd.setConstructorArgumentValues(cons);
    }


    private void handlerComponentField(PreBuildBeanFactory factory,Class<?> clazz, GeneralBeanDefinition bd) {
        Field[] fields = clazz.getDeclaredFields();
        List<PropertyValue>pvs = new ArrayList<>();
        for (Field field:fields) {
            field.setAccessible(true);
            if(field.isAnnotationPresent(Value.class)){
               String value = field.getAnnotation(Value.class).value();
               PropertyValue pv = new PropertyValue(field.getName(),value);
               pvs.add(pv);
            }else if(field.isAnnotationPresent(Autowired.class)){
                try {
                    PropertyValue pv = new PropertyValue(field.getName(),new BeanReference(field.getName()));
                    pvs.add(pv);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        bd.setPropertyValues(pvs);
    }

    private String handlerComponentValue(Class<?> clazz, GeneralBeanDefinition bd) {
        String bdName = "";
        String scope = "";
        String initMethod = "";
        String destroyMethod = "";
        if(clazz.getAnnotation(Component.class)!=null){
            bdName = clazz.getAnnotation(Component.class).value();
            scope = clazz.getAnnotation(Component.class).scope();
            initMethod = clazz.getAnnotation(Component.class).initMethod();
            destroyMethod = clazz.getAnnotation(Component.class).destroyMethod();
        }
        if(StringUtils.isBlank(bdName)){
            bdName = Common.toLowerCaseFirstOne(clazz.getSimpleName());
        }
        if(StringUtils.isNotBlank(initMethod)){
            bd.setInitMethodName(initMethod);
        }
        if(StringUtils.isNotBlank(destroyMethod)){
            bd.setDestroyMethodName(destroyMethod);
        }
        if(StringUtils.isNotBlank(scope)&&scope.equals(BeanDefinition.SCOPE_PROTOTYPE)){
            bd.setInitMethodName(scope);
        }
        return bdName;
    }

    private List<String> basePackageScan(String packName){
        List<String>classNames = new ArrayList<String>();
        URL url = this.getClass().getClassLoader().getResource(packName.replaceAll("\\.", "/"));
        String fileUrl = url.getFile()+"/";// E:/workspace/com/spring/mvc/
        File file = new File(fileUrl);//把路径转为文件目录
        String[] fileStr = file.list();//列出文件目录所有文件和文件夹的名字
        for (String path : fileStr) {//遍历所有的文件或文件夹的名字
            File pathFile = new File(fileUrl+path);//根据url和名字拼装成完整的url路径E:/workspace/com/spring/mvc
            if(pathFile.isDirectory()){
                basePackageScan(packName+"."+path);
            }else{//class 文件
                //mybatis.mapper.UserMapper
                classNames.add(packName+"."+pathFile.getName().replace(".class", ""));
            }
        }
        return classNames;
    }


}
